home *** CD-ROM | disk | FTP | other *** search
/ Developer Helper 1: Phil & Dave's Excellent CD / Excellent CD HFS.raw / Moof / Goodies / HyperCard Goodies / Serial Toolkit / Source Code / SPortUtil.inc < prev    next >
Text File  |  1988-11-18  |  9KB  |  276 lines

  1. {        Miscellaneous routines used with the serial port XCMDs.
  2.  
  3.     This file is included in the other serial port XCMD source code files -- i.e., it is not compiled and
  4.     linked separately.
  5.  
  6.     Copyright © 1987,88 Apple Computer, Inc.
  7.  
  8.     Initial coding 9/87 by Harry Chesley.
  9. }
  10.  
  11. const
  12.  
  13. GLOBALNAME = 'SPortGlobals';            { Where to store a handle to our globals. }
  14. MAXPARMBLKS = 4;                            { Maximum number of outgoing parameter blocks per port. }
  15. WRAPCOLUMN = 75;                            { The column at which to autowrap, if we are autowrapping. }
  16. MAXWRAP = 30;                                { The maximum number of characters to carry forward when autowrapping. }
  17.  
  18. type
  19.  
  20. { Serial port global data: }
  21. PortDataType =
  22.     record
  23.         isOpen: boolean;                                { Whether the port is open yet. }
  24.         portInDev, portOutDev: integer;        { The device numbers for input and output. }
  25.         byteConfig: integer;                            { The byte format being used. }
  26.         shakes: SerShk;                                { Flow control, etc. }
  27.         sendLFs: boolean;                                { Whether to send linefeeds after carriage returns. }
  28.         doEcho: boolean;                                { Whether to echo input. }
  29.         doEdit: boolean;                                { Whether to allow backspace editing on input. }
  30.         stripIncoming: boolean;                        { Whether to strip the top bit on incoming bytes. }
  31.         stripControls: boolean;                        { Whether to strip out non-return, non-tab control characters. }
  32.         autoWrap: boolean;                            { Auto-wrap input and output. }
  33.         currentColumn: integer;                    { Column we're about to write into. }
  34.         inputBuffer: Ptr;                                { The input buffer, or nil if there is none allocated. }
  35.         parmBlks: array [1..MAXPARMBLKS] of ParmBlkPtr;    { Asynch IO blocks (for output). }
  36.     end;
  37.  
  38. OurGlobalHandle = ^OurGlobalPtr;
  39. OurGlobalPtr = ^OurGlobalType;
  40.  
  41. { Global data: }
  42. OurGlobalType =
  43.     record
  44.         selectedPort: SPortSel;                                { Which port is selected. }
  45.         ports: array [SPortSel] of PortDataType;        { The port data for each port. }
  46.     end;
  47.  
  48. var
  49.  
  50. { Global data. (Note: Although this is called global, it's actually allocated as local to the top-level
  51.     XCMD routine, and therefore only stays around for the duration of the XCMD execution. Being
  52.     able to think of it as globals, and not having to pass it to each subroutine called, however, is
  53.     extremely useful. This is perhaps the best reason for using Pascal rather than C for writing
  54.     XCMDs.) }
  55.  
  56. Globals: OurGlobalHandle;        { Global data (this handle is saved in a HyperTalk global between XCMDs). }
  57. ThisSPort: PortDataType;        { A temporary copy of the global data for the currently selected port. }
  58.  
  59. procedure GetStrGlobal(name: str255; var glob: str255);
  60.     { Set glob to the global string specified by name. }
  61.  
  62.     var globHand: Handle;
  63.  
  64.     begin
  65.         { Get the HyperTalk global. }
  66.         globHand := GetGlobal(name);
  67.         { Convert it to a Pascal string. }
  68.         if globHand = nil then glob := ''
  69.         else
  70.             begin
  71.                 ZeroToPas(globHand^,glob);
  72.                 DisposHandle(globHand);
  73.             end;
  74.     end;
  75.  
  76. function GetLongGlobal(name: str255): longInt;
  77.     { Return the global string specified by name, interpreted as a long integer. }
  78.  
  79.     var globStr: str255;
  80.  
  81.     begin
  82.         { Get the HyperTalk global into a Pascal string. }
  83.         GetStrGlobal(name,globStr);
  84.         { Convert it to a longInt. }
  85.         GetLongGlobal := StrToLong(globStr);
  86.     end;
  87.  
  88. procedure SetStrGlobal(name: str255; glob: str255);
  89.     { Set the global string specified by name to glob. }
  90.  
  91.     var globHand: Handle;
  92.  
  93.     begin
  94.         { Convert the string to a HyperTalk style handle-string. }
  95.         globHand := PasToZero(glob);
  96.         { Set the global. }
  97.         SetGlobal(name,globHand);
  98.         { Dispose of our copy. }
  99.         DisposHandle(globHand);
  100.     end;
  101.  
  102. procedure SetLongGlobal(name: str255; globLong: longInt);
  103.     {Set the global string specified by name to a string that represents the number in globLong. }
  104.  
  105.     var globStr: str31;
  106.  
  107.     begin
  108.         { Convert the longInt to a Pascal string. }
  109.         globStr := LongToStr(globLong);
  110.         { Set the HyperTalk global to that. }
  111.         SetStrGlobal(name,globStr);
  112.     end;
  113.  
  114. procedure SetUpSPortGlobals;
  115.     { Create the handle we use for our global variables, and remember it in a HyperCard global. }
  116.  
  117.     var portIndex: SPortSel;
  118.  
  119.     begin
  120.         { Get the value of the global that holds our globals handle. }
  121.         Globals := OurGlobalHandle(GetLongGlobal(GLOBALNAME));
  122.         { If it's empty (which will evaluate to zero or nil), then we need to create it. }
  123.         if Globals = nil then
  124.             begin
  125.                 { Make the handle. }
  126.                 Globals := OurGlobalHandle(NewHandle(sizeof(OurGlobalType)));
  127.                 if Globals = nil then Fail('could not allocate global variable space');
  128.                 { Default to modem port. }
  129.                 Globals^^.selectedPort := sPortA;
  130.                 { Mark both ports as closed. }
  131.                 for portIndex := sPortA to sPortB do
  132.                     with Globals^^.ports[portIndex] do
  133.                         begin
  134.                             isOpen := false;
  135.                             { Set the device numbers. }
  136.                             case portIndex of
  137.                                 sPortA:
  138.                                     begin portInDev := -6; portOutDev := -7; end;
  139.                                 sPortB:
  140.                                     begin portInDev := -8; portOutDev := -9; end;
  141.                                 end;
  142.                             { Set the default byte format. }
  143.                             byteConfig := baud1200+stop10+noParity+data8;
  144.                             { Set the rest of the port defaults. }
  145.                             with shakes do
  146.                                 begin
  147.                                     fXOn := 0; fCTS := 0; errs := 0; evts := 0; fInX := 0;
  148.                                 end;
  149.                             { Set no auto-linefeed, no echo, no edit, no strip top bits. }
  150.                             sendLFs := false;
  151.                             doEcho := false;
  152.                             doEdit := false;
  153.                             stripIncoming := true;
  154.                             stripControls := false;
  155.                             autoWrap := false;
  156.                             currentColumn := 1;
  157.                             { No input buffer (i.e., use the default port buffer). }
  158.                             inputBuffer := nil;
  159.                         end;
  160.                 { Store our globals handle in the HyperTalk global, so we can get it later. }
  161.                 SetLongGlobal(GLOBALNAME,ord4(Globals));
  162.             end;
  163.         { Copy the globals for the active port into a more easily accessed location. }
  164.         ThisSPort := Globals^^.ports[Globals^^.selectedPort];
  165.     end;
  166.  
  167. procedure EnsureOpenPort;
  168.     { Check if the port is open. If it isn't, then open it now. }
  169.  
  170.     var i: integer;
  171.         actualIn, actualOut: integer;
  172.         whichPort: SPortSel;
  173.  
  174.     begin
  175.         if not ThisSPort.isOpen then
  176.             begin
  177.                 { Get the port. }
  178.                 whichPort := Globals^^.selectedPort;
  179.  
  180.                 { Allocate and init a gaggle of parmBlks for use with asynchronous output. }
  181.                 for i := 1 to MAXPARMBLKS do
  182.                     begin
  183.                         Globals^^.ports[whichPort].parmBlks[i] := ParmBlkPtr(NewPtr(sizeof(ParamBlockRec)));
  184.                         with Globals^^.ports[whichPort].parmBlks[i]^ do
  185.                             begin
  186.                                 ioCompletion := nil;
  187.                                 ioResult := noErr;
  188.                                 ioRefNum := Globals^^.ports[whichPort].portOutDev;
  189.                                 ioBuffer := nil;
  190.                             end;
  191.                     end;
  192.                 { Copy the new info into the current port. }
  193.                 ThisSPort := Globals^^.ports[whichPort];
  194.  
  195.                 { Actually open the port. }
  196.                 if Globals^^.selectedPort = sPortA then
  197.                     begin
  198.                         if (OpenDriver('.AOut',actualOut) <> noErr) or (OpenDriver('.AIn',actualIn) <> noErr) then
  199.                             Fail('OpenDriver failed');
  200.                     end
  201.                 else
  202.                     begin
  203.                         if (OpenDriver('.BOut',actualOut) <> noErr) or (OpenDriver('.BIn',actualIn) <> noErr) then
  204.                             Fail('OpenDriver failed');
  205.                     end;
  206.                 with ThisSPort do
  207.                     begin
  208.                         { Be paranoid about making sure everything is opened properly. }
  209.                         if (actualOut <> portOutDev) or (actualIn <> portInDev) then Fail('openDriver failed');
  210.                         { Set the defaults. }
  211.                         if SerHShake(portInDev,shakes) <> noErr then Fail('SerHShake failed');
  212.                         if SerHShake(portOutDev,shakes) <> noErr then Fail('SerHShake failed');
  213.                         if (SerReset(portInDev,byteConfig) <> noErr) or (SerReset(portOutDev,byteConfig) <> noErr) then
  214.                             Fail('SerReset failed');
  215.                         Globals^^.ports[Globals^^.selectedPort].isOpen := true;
  216.                         isOpen := true;
  217.                     end;
  218.             end;
  219.     end;
  220.  
  221. procedure GetStrParm(n: integer; var str: str255);
  222.     { Get the nth parameter into str. }
  223.  
  224.     begin
  225.         ZeroToPas(paramPtr^.params[n]^,str);
  226.     end;
  227.  
  228. function GetLongParm(n: integer): longInt;
  229.     { Return the nth parameter string, interpreted as a long integer. }
  230.  
  231.     var str: str255;
  232.  
  233.     begin
  234.         ZeroToPas(paramPtr^.params[n]^,str);
  235.         GetLongParm := StrToNum(str);
  236.     end;
  237.  
  238. function FindFreeIOBlk: ParmBlkPtr;
  239.     { Find and return a free I/O parameter block. If none are free, return nil. }
  240.  
  241.     var i: integer;
  242.  
  243.     begin
  244.     for i := 1 to MAXPARMBLKS do
  245.         if ThisSPort.parmBlks[i]^.ioResult <= noErr then
  246.             begin
  247.                 FindFreeIOBlk := ThisSPort.parmBlks[i];
  248.                 exit(FindFreeIOBlk);
  249.             end;
  250.     FindFreeIOBlk := nil;
  251.     end;
  252.  
  253. function WaitForFreeIOBlk: ParmBlkPtr;
  254.     { Wait for an I/O block to be free and return it. }
  255.  
  256.     var theBlock: ParmBlkPtr;
  257.  
  258.     begin
  259.         repeat theBlock := FindFreeIOBlk until theBlock <> nil;
  260.         WaitForFreeIOBlk := theBlock;
  261.     end;
  262.  
  263. procedure WaitForAllFree;
  264.     { Wait for all I/O blocks to free up (i.e., wait for all output to finish). }
  265.  
  266.     var allDone: boolean;
  267.         i: integer;
  268.  
  269.     begin
  270.         repeat
  271.             allDone := true;
  272.             for i := 1 to MAXPARMBLKS do
  273.                 if ThisSPort.parmBlks[i]^.ioResult > noErr then allDone := false;
  274.         until allDone;
  275.     end;
  276.